import com.gwc.intervaltree.DateInterval;
import com.gwc.intervaltree.Interval;
import com.gwc.intervaltree.IntervalTree;

import java.sql.ResultSet;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

// 获取、处理与弧段以及装备相关的数据

/* 弧段原始数据格式说明
  `ID` int(0) NOT NULL AUTO_INCREMENT, 主键
  `IdSat` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '目标代号',
  `IdDev` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '设备代号',
  `StartTimeUtc` datetime(0) NULL DEFAULT NULL COMMENT '进站开始时间',
  `StartElevationDeg` double NULL DEFAULT NULL, 开始仰角（0-90）
  `EndPTimeUtc` datetime(0) NULL DEFAULT NULL COMMENT '出站结束时间',
  `EndPElevationDeg` double NULL DEFAULT NULL, 结束仰角（0-90）
  `TopPElevationDeg` double NULL DEFAULT NULL, 最大仰角（0-90）
  `Mark` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 升降轨标识-RISE/FALL
  `Rev` int(0) NULL DEFAULT NULL, 圈数

  补充字段：
  持续时间（秒）
  cost 初始化0
  score 初始化0
 */

/* 装备能力数据格式说明
  Key-装备ID

  Value：
  `DEVTYPE` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '装备类型',
  `DEVWORKPATTORN` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '工作模式',
  `MultipleTarget` int(0) NULL DEFAULT NULL COMMENT '多目标数',
  `STARTUPTIME` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '开机时间',
  `SHUTDOWNTIME` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '关机时间',
  `CONTINUETRACKING` bigint(0) NULL DEFAULT NULL COMMENT '连续跟踪时长',
  `TASKINTERVAL` bigint(0) NULL DEFAULT NULL COMMENT '任务间隔',
  `BREATHINGSPACE` bigint(0) NULL DEFAULT NULL COMMENT '休息时间',
  `MAXWORKTIME` bigint(0) NULL DEFAULT NULL COMMENT '最大工作时长',
  `PATTERNSWITCHTIME` bigint(0) NULL DEFAULT NULL COMMENT '模式切换时间',
  `POSITIVEROTATIONTIME` bigint(0) NULL DEFAULT NULL COMMENT '正面转动时间',
  `MISSIONPREPARATIONTIME` bigint(0) NULL DEFAULT NULL COMMENT '任务准备时间',
 */

/* 已调度弧段数据格式说明
  `TARGETID` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '目标编号',
  `DEVICEID` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '设备编号',
  `TASKSTARTTIME` datetime(0) NULL DEFAULT NULL COMMENT '任务开始时刻',
  `TASKENDTIME` datetime(0) NULL DEFAULT NULL COMMENT '任务结束时刻',
  `ARCID` int(0) NULL DEFAULT NULL COMMENT '弧段ID',
  `SENDTIME` datetime(0) NULL DEFAULT NULL COMMENT '生成时间',
  `PLANSTATU` int(0) NULL DEFAULT NULL COMMENT '任务状态：未下发  已下发  已取消 已执行 无数据（超过任务时间并且未收到执行数据） 执行成功',
  `DEVMOUDLE` varchar(255) CHARACTER SET gbk COLLATE gbk_bin NULL DEFAULT NULL COMMENT '工作模式',
  `EMERGENCYTYPE` tinyint(0) NULL DEFAULT NULL COMMENT '应急 1/非应急 0'
*/


import java.util.HashMap;

public class Data {
    private JDBCUtil instance;
    private static Data data;
    ArrayList<String[]> arcData;//存放弧段原始数据
    HashMap<String, String[]> devData;//存放装备原始数据
    HashSet<String> scheduledArcs;//存放已调度的弧段ID集合
    HashMap<String, ArrayList<String[]>> sat_arcs;// 目标——弧段集合
    HashMap<String, ArrayList<String[]>> dev_arcs;// 装备——弧段集合
    HashMap<String, IntervalTree<Date>> intervalTreeByDev;//装备-区间树映射关系，每一装备对应构建一棵区间树
    HashMap<String, ArrayList<String[]>> dev_timeAxis;//装备-已调度弧段时间轴（有序）
    HashMap<String, ArrayList<String[]>> sat_timeAxis;//目标-已调度弧段时间轴（有序）
    HashMap<String, HashMap<String, HashMap<String, ArrayList<String[]>>>> sat_circle_dev_arcs;//目标-圈数-设备-弧段集合
    HashMap<String, HashMap<String, IntervalTree<Date>>> dev_mode_intervalTree;//装备-工作模式-区间树，每一装备的每一工作模式对应构建一棵区间树
    public final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//日期时间格式

    public Data() {
        instance = JDBCUtil.getInstance();
    }

    /**
     * 单例模式构建访问Data的访问对象
     *
     * @return
     */
    public static Data getData() throws ParseException {
        if (data == null) {
            data = new Data();
            data.getDevData();
            data.getScheduledData();
            data.getArcData();
            data.getIntervalTreeByDev();
            data.calculateCost();
            data.sortByCost();
            data.buildIndex();
        }
        return data;
    }

    //计算时间差（秒）
    public long calculateTime(String startTime, String endTime) throws ParseException {
        Date d1 = df.parse(startTime);
        Date d2 = df.parse(endTime);
        long diff = d2.getTime() - d1.getTime();
        return diff / 1000;
    }

    //二分法插入时间节点（有序）
    public int binaryInsertTime(ArrayList<String[]> arcs, String id, String time, String mark, String devMode) throws ParseException {
        Date time_ = df.parse(time);
        int left = 0;
        int right = arcs.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (time_.after(df.parse(arcs.get(mid)[1]))) {
                left = mid + 1;
            } else if (time_.before(df.parse(arcs.get(mid)[1]))) {
                right = mid - 1;
            } else {
                arcs.add(mid, new String[]{id, time, mark, devMode});
                return mid;
            }
        }
        arcs.add(left, new String[]{id, time, mark, devMode});
        return left;
    }

    //获取原始弧段数据
    public void getArcData() {
        System.out.println("获取原始弧段数据：" + df.format(new Date()));
        this.arcData = new ArrayList<>();
        //获取弧段信息
        String sql = "SELECT * FROM zbdd_data_arcs";
        ResultSet rs = null;
        try {
            instance.getConnection();
            rs = instance.executeQuetyBYStatement(sql);
            //如果有数据，rs.next()返回true
            while (rs.next()) {
                String[] arc = new String[]{rs.getString("ID"), rs.getString("IdSat"), rs.getString("IdDev"), rs.getString("StartTimeUtc"), rs.getString("StartElevationDeg"), rs.getString("EndPTimeUtc"), rs.getString("EndPElevationDeg"), rs.getString("TopPElevationDeg"), rs.getString("Mark"), rs.getString("Rev"), String.valueOf(this.calculateTime(rs.getString("StartTimeUtc"), rs.getString("EndPTimeUtc"))), "0", "0"};
                this.arcData.add(arc);//从数据中截取可用信息，放入arcData
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("获取原始弧段数据完成：" + df.format(new Date()));
    }

    //获取装备能力数据
    public void getDevData() {
        System.out.println("获取装备能力数据：" + df.format(new Date()));
        this.devData = new HashMap<>();
        //获取装备信息
        String sql = "SELECT * FROM zbdd_dev_basic_condition LEFT JOIN zbdd_device_status_constraint zdsc ON zbdd_dev_basic_condition.DevCode = zdsc.DEVICECODE;";
        ResultSet rs = null;
        try {
            instance.getConnection();
            rs = instance.executeQuetyBYStatement(sql);
            //如果有数据，rs.next()返回true
            while (rs.next()) {
                //从数据中截取可用信息，放入devData
                this.devData.put(rs.getString("DevCode"), new String[]{rs.getString("DevType"), rs.getString("DevMoudle"), rs.getString("MultipleTarget"), rs.getString("STARTUPTIME"), rs.getString("SHUTDOWNTIME"), rs.getString("CONTINUETRACKING"), rs.getString("TASKINTERVAL"), rs.getString("BREATHINGSPACE"), rs.getString("MAXWORKTIME"), rs.getString("PATTERNSWITCHTIME"), rs.getString("POSITIVEROTATIONTIME"), rs.getString("MISSIONPREPARATIONTIME")});
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("获取装备能力数据完成：" + df.format(new Date()));
    }

    //获取已调度数据
    public void getScheduledData() {
        System.out.println("获取已调度数据：" + df.format(new Date()));
        this.scheduledArcs = new HashSet<>();
        this.dev_timeAxis = new HashMap<>();
        this.sat_timeAxis = new HashMap<>();
        this.dev_mode_intervalTree = new HashMap<>();
        //获取已调度数据信息
        String sql = "select * from zbdd_scheduling_plan_provisional;";
        ResultSet rs = null;
        try {
            instance.getConnection();
            rs = instance.executeQuetyBYStatement(sql);
            //如果有数据，rs.next()返回true
            while (rs.next()) {
                String[] arc = new String[]{rs.getString("TARGETID"), rs.getString("DEVICEID"), rs.getString("TASKSTARTTIME"), rs.getString("TASKENDTIME"), rs.getString("ARCID"), rs.getString("SENDTIME"), rs.getString("PLANSTATU"), rs.getString("DEVMOUDLE"), rs.getString("EMERGENCYTYPE")};
                this.scheduledArcs.add(arc[4]);//加入已调度弧段集合
                if (!this.dev_timeAxis.containsKey(arc[1])) {
                    //对应装备，建立时间轴
                    this.dev_timeAxis.put(arc[1], new ArrayList<>());
                }
                binaryInsertTime(this.dev_timeAxis.get(arc[1]), arc[4], arc[3], "-1", arc[7]);// 插入结束时间节点
                binaryInsertTime(this.dev_timeAxis.get(arc[1]), arc[4], arc[2], "1", arc[7]);// 插入开始时间节点
                if (!this.dev_mode_intervalTree.containsKey(arc[1])) {
                    this.dev_mode_intervalTree.put(arc[1], new HashMap<>());
                }
                if (!this.dev_mode_intervalTree.get(arc[1]).containsKey(arc[7])) {
                    IntervalTree<Date> tree = new IntervalTree<>();
                    this.dev_mode_intervalTree.get(arc[1]).put(arc[7], tree);//为每个装备的每一工作模式构建一棵对应的区间树，加入索引
                }
                this.dev_mode_intervalTree.get(arc[1]).get(arc[7]).add(new DateInterval(df.parse(arc[2]), df.parse(arc[3]), Interval.Bounded.CLOSED, arc[4]));//向区间树中添加区间
                if (!this.sat_timeAxis.containsKey(arc[0])) {
                    //对应目标，建立时间轴
                    this.sat_timeAxis.put(arc[0], new ArrayList<>());
                }
                binaryInsertTime(this.sat_timeAxis.get(arc[0]), arc[4], arc[3], "-1", arc[7]);// 插入结束时间节点
                binaryInsertTime(this.sat_timeAxis.get(arc[0]), arc[4], arc[2], "1", arc[7]);// 插入开始时间节点
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("获取已调度数据完成：" + df.format(new Date()));
    }

    //根据每个装备构建对应的区间树
    public void getIntervalTreeByDev() throws ParseException {
        System.out.println("根据每个装备构建对应的区间树：" + df.format(new Date()));
        this.intervalTreeByDev = new HashMap<>();
        for (String[] arc : this.arcData) {
            if (!this.intervalTreeByDev.containsKey(arc[2])) {
                IntervalTree<Date> tree = new IntervalTree<>();
                this.intervalTreeByDev.put(arc[2], tree);//为每个装备构建一棵对应的区间树，加入索引
            }
            this.intervalTreeByDev.get(arc[2]).add(new DateInterval(df.parse(arc[3]), df.parse(arc[5]), Interval.Bounded.CLOSED, arc[0]));//向区间树中添加区间
        }
        System.out.println("每个装备构建对应的区间树完成：" + df.format(new Date()));
    }

    //计算弧段代价
    public void calculateCost() throws ParseException {
        System.out.println("计算弧段代价：" + df.format(new Date()));
        //计算冲突数，更新cost值和score值
        for (String[] arc : this.arcData) {
            //cost=冲突数
            arc[11] = String.valueOf(intervalTreeByDev.get(arc[2]).query(new DateInterval(df.parse(arc[3]), df.parse(arc[5]), Interval.Bounded.CLOSED, arc[0])).size());
            //score=cost+时间窗口持续时间
//            arc[12] = String.valueOf(Integer.parseInt(arc[10]) + Integer.parseInt(arc[11]));
        }
        System.out.println("弧段代价计算完成：" + df.format(new Date()));
    }

    //弧段按代价排序
    public void sortByCost() {
        System.out.println("弧段按代价排序：" + df.format(new Date()));
        Collections.sort(this.arcData, Comparator.comparingInt(s -> Integer.parseInt(s[11])));
        System.out.println("弧段按代价排序完成：" + df.format(new Date()));
    }

    //建立数据映射结构
    public void buildIndex() {
        System.out.println("建立数据映射结构：" + df.format(new Date()));
        this.sat_arcs = new HashMap<>();
        this.dev_arcs = new HashMap<>();
        this.sat_circle_dev_arcs = new HashMap<>();
        for (String[] arc : this.arcData) {
            //根据已调度数据筛选弧段
            //根据装备能力数据筛选弧段
            if (!this.scheduledArcs.contains(arc[0]) && this.devData.containsKey(arc[2]) && Integer.parseInt(arc[10]) <= Integer.parseInt(this.devData.get(arc[2])[5]) && Integer.parseInt(arc[10]) <= Integer.parseInt(this.devData.get(arc[2])[8])) {
                if (!this.sat_arcs.containsKey(arc[1])) {
                    this.sat_arcs.put(arc[1], new ArrayList<>());
                }
                this.sat_arcs.get(arc[1]).add(arc);
                if (!this.dev_arcs.containsKey(arc[2])) {
                    this.dev_arcs.put(arc[2], new ArrayList<>());
                }
                this.dev_arcs.get(arc[2]).add(arc);
                if (!this.sat_circle_dev_arcs.containsKey(arc[1])) {
                    this.sat_circle_dev_arcs.put(arc[1], new HashMap<>());
                }
                if (!this.sat_circle_dev_arcs.get(arc[1]).containsKey(arc[9])) {
                    this.sat_circle_dev_arcs.get(arc[1]).put(arc[9], new HashMap<>());
                }
                if (!this.sat_circle_dev_arcs.get(arc[1]).get(arc[9]).containsKey(arc[2])) {
                    this.sat_circle_dev_arcs.get(arc[1]).get(arc[9]).put(arc[2], new ArrayList<>());
                }
                this.sat_circle_dev_arcs.get(arc[1]).get(arc[9]).get(arc[2]).add(arc);
            }
        }
        System.out.println("建立数据映射结构完成：" + df.format(new Date()));
    }
}
